<?php
// +-----------------------------------------------------------------------------------------------
// | 简易CMS
// +-----------------------------------------------------------------------------------------------
// | [请手动修改文件描述]
// +-----------------------------------------------------------------------------------------------
// | Author: IT果农 <htmambo@163.com> <http://www.haolie.net>
// +-----------------------------------------------------------------------------------------------
// | Version $Id: Auth.class.php 9 2016-09-17 11:22:17Z IT果农 <htmambo@163.com> $
// +-----------------------------------------------------------------------------------------------

namespace CMS\Wechat;

use CMS\Wechat\WechatErrCode;
/**
 * 微信SDK授权处理基础类
 */
class Auth {
    /**
     * 微信开发者申请的appID
     * @var string
     */
    var $appId = '';

    /**
     * 微信开发者申请的appSecret
     * @var string
     */
    var $appSecret = '';

    /**
     * 获取到的access_token
     * @var string
     */
    var $accessToken = '';
    var $__atk = 0;

    /**
     * 微信api根路径
     * @var string
     */
    var $apiURL = 'https://api.weixin.qq.com/cgi-bin';

    /**
     * 构造方法，调用微信高级接口时实例化SDK
     * @param string $appid  微信appid
     * @param string $secret 微信appsecret
     * @param string $token  获取到的access_token
     */
    public function __construct($appid, $secret = null, $token = null) {
        if (is_array($appid)) {
            if (isset($appid['token'])) {
                $token = $appid['token'];
            }
            $secret = $appid['appsecret'];
            $appid = $appid['appid'];
        }
        if ($appid && $secret) {
            $this->appId = $appid;
            $this->appSecret = $secret;

            if (!empty($token)) {
                $this->accessToken = $token;
            }
        } else {
            throw new \Exception('缺少参数 APP_ID 和 APP_SECRET!');
        }
    }

    /**
     * 主要是用来获取及保存微信的access_token
     * @param type $name
     */
    function __get($name) {
        if ($name == 'accessToken') {
            return $this->getAccessToken();
        }
    }
    function getAccessToken($code = null, $force = false) {
    }
    /**
     * 调用微信api获取响应数据
     * @param  string $name   API名称
     * @param  string $data   POST请求数据
     * @param  string $method 请求方式
     * @param  string $param  GET请求参数
     * @return array          api返回结果
     */
    public function api($name, $data = '', $method = 'POST', $param = '', $json = true) {
        if (!$this->accessToken) {
            $this->accessToken = $this->getAccessToken();
        }
        $params = array('access_token' => $this->accessToken);

        if (!empty($param) && is_array($param)) {
            $params = array_merge($params, $param);
        }

        $url = "{$this->apiURL}/{$name}";
        if ($json && !empty($data)) {
            //保护中文，微信api不支持中文转义的json结构，顺便处理几个特殊的值
            if (is_string($data)) {
                $data = json_decode($data);
            }
            array_walk_recursive($data, function(&$value) {
                if(!is_array($value) && !is_object($value)){ //有可能是多维数组
                    $value = urlencode($value);
                    if ($value === true) {
                        $value = 'true';
                    } else if ($value === false) {
                        $value = 'false';
                    } else if ($value === null) {
                        $value = 'null';
                    }
                }
            });
            $data = urldecode(json_encode($data));
            $data = str_replace('"true"', 'true', $data);
            $data = str_replace('"false"', 'false', $data);
            $data = str_replace('"null"', 'null', $data);
        }
        $data = self::http($url, $params, $data, $method);
        $result = json_decode($data, true);
        $log = array(
            'url'    => $url, 'params' => $param, 'result' => $result, 'return' => $data
        );
        if (isset($result['errcode']) && $result['errcode'] != 0) {
            if ($result['errcode'] === 40001 || $result['errcode'] === 40014 || $result['errcode'] === 42001 || $result['errcode'] === 41001) {
                //如果错误是由于access_token引起地，那么就再强制性的获取一次，这玩意也有每日次数限制，所以得保存一下
                if ($this->__atk == 0) {
                    $this->__atk ++;
                    $this->accessToken = $this->getAccessToken(NULL, true);
                    return $this->api($name, $data, $method, $param, $json);
                }
            }
            $result['message'] = WechatErrCode::getErrText($result['errcode']);
            //throw new \Exception(WechatErrCode::getErrText($result['errcode']));
        } else {
            unset($result['errcode']);
        }

        return $result;
    }

    /**
     * 发送HTTP请求方法，目前只支持CURL发送请求
     * @param  string $url    请求URL
     * @param  array  $param  GET参数数组
     * @param  array  $data   POST的数据，GET请求时该参数无效
     * @param  string $method 请求方法GET/POST
     * @return array          响应数据
     */
    protected static function http($url, $param, $data = '', $method = 'GET') {
        $opts = array(
            CURLOPT_TIMEOUT        => 30,
            CURLOPT_RETURNTRANSFER => 1,
            CURLOPT_SSL_VERIFYPEER => false,
            CURLOPT_SSL_VERIFYHOST => false,
        );

        /* 根据请求类型设置特定参数 */
        $opts[CURLOPT_URL] = $url . '?' . http_build_query($param);
        if (strtoupper($method) == 'POST') {
            $opts[CURLOPT_POST] = 1;

            $opts[CURLOPT_POSTFIELDS] = $data;
            if (is_string($data)) { //发送JSON数据
                $opts[CURLOPT_HTTPHEADER] = array(
                    'Content-Type: application/json; charset=utf-8',
                    'Content-Length: ' . strlen($data),
                );
            }
        }

        /* 初始化并执行curl请求 */
        $ch = curl_init();
        curl_setopt_array($ch, $opts);
        $data = curl_exec($ch);
        $error = curl_error($ch);
        curl_close($ch);

        //发生错误，抛出异常
        if ($error) {
            throw new \Exception('请求发生错误：' . $error);
        }
        return $data;
    }

}
